namespace eval offline {
    set ::NS(offline) "http://jabber.org/protocol/offline"

    custom::defvar options(flexible_retrieval) 0 \
	[::msgcat::mc "Retrieve offline messages using POP3-like protocol."] \
	-type boolean -group Messages

}

proc offline::request_headers {connid} {
    variable options

    if {$options(flexible_retrieval)} {
	jlib::send_iq get \
	    [jlib::wrapper:createtag offline \
		 -vars [list xmlns $::NS(offline)]] \
	    -command [list [namespace current]::receive_headers $connid] \
	    -connection $connid
    }
}

hook::add connected_hook [namespace current]::offline::request_headers 9

proc offline::receive_headers {connid res child} {

    if {$res != "OK"} {
	return
    }

    fill_tree $connid $child
}

proc offline::open_window {} {
    global tcl_platform
    variable options

    set w .offline_messages

    if {[winfo exists $w]} {
	return
    }

    add_win $w -title [::msgcat::mc "Offline Messages"] \
	-tabtitle [::msgcat::mc "Offline Messages"] \
	-raisecmd [list focus $w.tree] \
	-class JDisco

    if {![info exists options(seencolor)]} {
	if {[cequal $tcl_platform(platform) unix] && \
		![string equal [option get $w disabledForeground JDisco] ""]} {
	    set options(seencolor) [option get $w disabledForeground JDisco]
	} else {
	    set options(seencolor) [option get $w featurecolor JDisco]
	}
    }
    if {![info exists options(unseencolor)]} {
	set options(unseencolor) [option get $w fill JDisco]
    }

    set sw [ScrolledWindow $w.sw]
    set tw [Tree $w.tree -deltax 16 -deltay 18 -dragenabled 0]
    $sw setwidget $tw

    pack $sw -side top -expand yes -fill both

    $tw bindText <ButtonPress-3> \
	    [list [namespace current]::message_popup $tw]
    $tw bindText <Double-ButtonPress-1> \
	    [list [namespace current]::message_action fetch $tw]

    # HACK
    bind $tw.c <Return> \
         "[namespace current]::message_action fetch $tw \[$tw selection get\]"
    bindscroll $tw.c
}

proc offline::fill_tree {connid child} {

    jlib::wrapper:splitxml $child tag vars isempty chdata children

    if {[lempty $children]} {
	return
    }
    
    set w .offline_messages

    if {![winfo exists $w]} {
	open_window
    }

    set tw $w.tree

    foreach ch $children {
	jlib::wrapper:splitxml $ch tag1 vars1 isempty1 chdata1 children1

	switch -- $tag1 {
	    item {
		set node [jlib::wrapper:getattr $vars1 node]
		set from [jlib::wrapper:getattr $vars1 from]
		set category [jlib::wrapper:getattr $vars1 category]
		set type [jlib::wrapper:getattr $vars1 type]
		add_message $tw $connid $node $from $category $type
	    }
	}
    }
}

package require md5

proc offline::add_message {tw connid node from category type} {
    global font
    variable options

    set jid [jlib::connection_jid $connid]

    set fnode [str2node $jid]
    if {![$tw exists $fnode]} {
	$tw insert end root $fnode -text $jid -open 1 \
	    -fill $options(unseencolor) -image browser/user -font $font \
	    -data [list type jid connid $connid jid $jid unseen 1]
    }

    set snode [str2node $node]
    if {![$tw exists $snode]} {
	if {$type == ""} {
	    set t ""
	} else {
	    set t " ($type)"
	}
	$tw insert end $fnode $snode -text "$category$t from $from \[$node\]" -open 1 \
	    -fill $options(unseencolor) -font $font \
	    -data [list type node connid $connid jid $jid node $node \
			from $from category $category type $type unseen 1]
	message_update $tw $snode
    }
}

proc offline::str2node {string} {
    set utf8str [encoding convertto utf-8 $string]
    if {[catch { ::md5::md5 -hex $utf8str } ret]} {
	return [::md5::md5 $utf8str]
    } else {
	return $ret
    }
}

proc offline::message_popup {tw node} {
    $tw selection set $node

    if {[catch { array set props [$tw itemcget $node -data] }]} {
	return
    }

    set m .offline_popup_menu

    if {[winfo exists $m]} {
	destroy $m
    }

    menu $m -tearoff 0

    switch -- $props(type) {
	jid {
	    $m add command -label [::msgcat::mc "Sort by from"] \
	        -command [list [namespace current]::message_action sortfrom $tw $node]
	    $m add command -label [::msgcat::mc "Sort by node"] \
	        -command [list [namespace current]::message_action sortnode $tw $node]
	    $m add command -label [::msgcat::mc "Sort by type"] \
	        -command [list [namespace current]::message_action sorttype $tw $node]
	    $m add command -label [::msgcat::mc "Fetch unseen messages"] \
		-command [list [namespace current]::message_action fetchunseen $tw $node]
	    $m add command -label [::msgcat::mc "Fetch all messages"] \
		-command [list [namespace current]::message_action fetch $tw $node]
	    $m add command -label [::msgcat::mc "Purge seen messages"] \
		-command [list [namespace current]::message_action purgeseen $tw $node]
	    $m add command -label [::msgcat::mc "Purge all messages"] \
		-command [list [namespace current]::message_action purge $tw $node]
	}
	node {
	    $m add command -label [::msgcat::mc "Fetch message"] \
		-command [list [namespace current]::message_action fetch $tw $node]
	    $m add command -label [::msgcat::mc "Purge message"] \
		-command [list [namespace current]::message_action purge $tw $node]
	}
	default {
	    return
	}
    }

    tk_popup $m [winfo pointerx .] [winfo pointery .]
}

proc offline::message_action {action tw node} {
    variable options

    if {[catch { array set props [$tw itemcget $node -data] }]} {
        return
    }

    switch -glob -- $props(type)/$action {
        node/fetch {
	    jlib::send_iq get \
		[jlib::wrapper:createtag offline \
		     -vars [list xmlns $::NS(offline)] \
		     -subtags [list [jlib::wrapper:createtag item \
					 -vars [list action view \
						     node $props(node)]]]] \
	    -connection $props(connid) \
	    -command [list [namespace current]::action_result $action $tw $node]
        }

	node/purge {
	    jlib::send_iq set \
		[jlib::wrapper:createtag offline \
		     -vars [list xmlns $::NS(offline)] \
		     -subtags [list [jlib::wrapper:createtag item \
					 -vars [list action remove \
						     node $props(node)]]]] \
	    -connection $props(connid) \
	    -command [list [namespace current]::action_result $action $tw $node]
	}

        jid/fetch {
	    jlib::send_iq get \
		[jlib::wrapper:createtag offline \
		     -vars [list xmlns $::NS(offline)] \
		     -subtags [list [jlib::wrapper:createtag fetch]]] \
	    -connection $props(connid) \
	    -command [list [namespace current]::action_result $action $tw $node]
        }

        jid/purge {
	    jlib::send_iq set \
		[jlib::wrapper:createtag offline \
		     -vars [list xmlns $::NS(offline)] \
		     -subtags [list [jlib::wrapper:createtag purge]]] \
	    -connection $props(connid) \
	    -command [list [namespace current]::action_result $action $tw $node]
        }

	jid/fetchunseen {
	    set q 0
	    set items {}
	    foreach child [$tw nodes $node] {
		catch { array unset props1 }
		if {![catch { array set props1 [$tw itemcget $child -data] }] && \
			$props1(unseen) > 0} {
		    lappend items [jlib::wrapper:createtag item \
				       -vars [list action view \
						   node $props1(node)]]
		} else {
		    set q 1
		}
	    }
	    if {$q} {
		if {![lempty $items]} {
		    jlib::send_iq get \
			[jlib::wrapper:createtag offline \
			     -vars [list xmlns $::NS(offline)] \
			     -subtags $items] \
			-connection $props(connid) \
			-command [list [namespace current]::action_result $action $tw $node]
		}
	    } else {
		message_action fetch $tw $node
	    }
	}

	jid/purgeseen {
	    if {$props(unseen) > 0} {
		set items {}
		foreach child [$tw nodes $node] {
		    catch { array unset props1 }
		    if {![catch { array set props1 [$tw itemcget $child -data] }]} {
			if {$props1(unseen) == 0} {
			    lappend items [jlib::wrapper:createtag item \
					     -vars [list action remove \
							 node $props1(node)]]
			}
		    }
		}
		if {![lempty $items]} {
		    jlib::send_iq set \
			[jlib::wrapper:createtag offline \
			     -vars [list xmlns $::NS(offline)] \
			     -subtags $items] \
			-connection $props(connid) \
			-command [list [namespace current]::action_result $action $tw $node]
		}
	    } else {
		message_action purge $tw $node
	    }
	}

        jid/sortfrom {
            sort_nodes $tw $node from
        }

        jid/sortnode {
            sort_nodes $tw $node node
        }

        jid/sorttype {
            sort_nodes $tw $node category type
        }

        default {
        }
    }
}

proc offline::sort_nodes {tw node type {subtype ""}} {
    set children {}
    foreach child [$tw nodes $node] {
        catch { unset props }
        array set props [$tw itemcget $child -data]

	if {$subtype == ""} {
	    lappend children [list $child $props($type)]
	} else {
	    lappend children \
		[list $child [list $props($type) $props($subtype)]]
	}
    }
    set neworder {}
    foreach child [lsort -index 1 $children] {
        lappend neworder [lindex $child 0]
    }
    $tw reorder $node $neworder
}

proc offline::action_result {action tw node res child} {
    variable options

    if {$res != "OK"} {
	return
    }

    if {[catch { array set props [$tw itemcget $node -data] }]} {
        return
    }

    switch -glob -- $props(type)/$action {
        node/fetch -
	node/fetchunseen {
            if {$props(unseen)} {
                set props(unseen) 0
                $tw itemconfigure $node -fill $options(seencolor) \
		    -data [array get props]
                message_update $tw $node
            }
        }

	node/purge {
            set props(unseen) 0
            $tw itemconfigure $node -fill $options(seencolor) \
		-data [array get props]
            message_update $tw $node

	    $tw delete $node
	}

	node/purgeseen {
            if {!$props(unseen)} {
		action_result purge $tw $node OK {}
	    }
	}

        jid/fetch -
	jid/fetchunseen {
	    foreach child [$tw nodes $node] {
		action_result $action $tw $child OK {}
	    }
        }

	jid/purge -
	jid/purgeseen {
	    foreach child [$tw nodes $node] {
		action_result $action $tw $child OK {}
	    }
	    if {[lempty [$tw nodes $node]]} {
		$tw delete $node
	    }
	}

        default {
        }
    }
}

proc offline::message_update {tw node} {
    variable options

    for {set parent [$tw parent $node]} \
            {![cequal $parent root]} \
            {set parent [$tw parent $parent]} {
        set unseen 0

        foreach child [$tw nodes $parent] {
            catch { unset props }
            array set props [$tw itemcget $child -data]

            incr unseen $props(unseen)
        }

        catch { unset props }
        array set props [$tw itemcget $parent -data]
        set props(unseen) $unseen

        set text $props(jid)
        set myfill $options(seencolor)
        if {$unseen > 0} {
            append text " ($unseen)"
            set myfill $options(unseencolor)
        }
        $tw itemconfigure $parent -text $text -fill $myfill \
                -data [array get props]
    }
}

